/**
 * 值处理
 * @type type
 */
function atValue(name, value, callback) {
	if (typeof name === 'object') {
		return each(name, callback);
	} else {
		return callback(name, value);
	}
}
/**
 * 元素处理
 * @param {Object|Element} element
 * @param {Function} callback
 * @returns {unresolved}
 */
function elements(element, callback) {
	if (element instanceof Element) {
		return callback(element);
	} else {
		return each(element, function(_, element) {
			return callback(element);
		});
	}
}
/**
 * 元素CSS读写操作
 * @param {Element} element
 * @param {String} name
 * @param {String} value
 * @returns {Element|String}
 */
function css(element, name, value) {
	return atValue(name, value, function(name, value) {
		if (value === undefined) {
			var _value;
			return elements(element, function(element) {
				return _value = element.style[name], false;
			}), _value;
		}
		return elements(element, function(element) {
			element.style[name] = value;
		}), element;
	});
}
/**
 * 元素属性读写操作
 * @param {Element} element
 * @param {String} name
 * @param {String} value
 * @returns {Element|String}
 */
function attr(element, name, value) {
	return atValue(name, value, function(name, value) {
		if (value === undefined) {
			var _value;
			return elements(element, function(element) {
				return _value = element.getAttribute(name), false;
			}), _value;
		}
		return elements(element, function(element) {
			element.setAttribute(name, value);
		}), element;
	});
}
/**
 * 循环处理
 * @param {Object} data
 * @param {Function} callback
 * @returns {undefined}
 */
function each(data, callback) {
	if (typeof data === 'object' && data) {
		if (data.length) {
			for (var key = 0; key < data.length; key++) {
				if (callback(key, data[key]) === false) {
					break;
				}
			}
		} else {
			for (var key in data) {
				if (callback(key, data[key]) === false) {
					break;
				}
			}
		}
	}
}
/**
 * 元素类名处理
 * @param {Element} element
 * @param {String} name
 * @param {Function} callback
 * @returns {Array}
 */
function elementClass(element, name, callback) {
	var _class = (element.className || '').split(/\s+/g),
		_name = name.split(/\s+/g);
	return each(_name, function(_, val) {
		if (val) {
			return callback(_class.indexOf(val), val);
		}
	}), _class;
}
/**
 * 是否存在类名
 * @param {Element} element
 * @param {String} name
 * @returns {undefined}
 */
function hasClass(element, name) {
	var _has = false;
	return elementClass(element, name, function(key) {
		if (key >= 0) {
			return _has = true, false;
		}
	}), _has;
}
/**
 * 添加类名
 * @param {Element} element
 * @param {String} name
 * @returns {undefined}
 */
function addClass(element, name) {
	var _class = [],
		old = elementClass(element, name, function(key, val) {
			if (key < 0) {
				_class.push(val);
			}
		});
	element.className = _class.concat(old).join(' ');
}
/**
 * 移除类名
 * @param {Element} element
 * @param {String} name
 * @returns {undefined}
 */
function removeClass(element, name) {
	var _hasClass = [],
		old = elementClass(element, name, function(key, val) {
			if (key >= 0) {
				_hasClass.push(val);
			}
		});
	each(_hasClass.reverse(), function(_, val) {
		old.splice(old.indexOf(val), 1);
	});
	element.className = old.join(' ');
}
/**
 * 添加子元素
 * @param {Element} element
 * @param {Object} childs
 * @returns {undefined}
 */
function append(element, childs) {
	if (childs instanceof Element) {
		for (var key = 1; key < arguments.length; key++) {
			element.appendChild(arguments[key]);
		}
	} else {
		each(childs, function(_, child) {
			element.appendChild(child);
		});
	}
}
/**
 * 创建元素
 * @param {String} tag
 * @param {Object} _attr
 * @param {Object} _css
 * @returns {Element}
 */
function create(tag, _attr, _css) {
	var element = document.createElement(tag);
	return attr(element, _attr), css(element, _css), element;
}
/**
 * 添加事件
 * @param {Element} element 元素
 * @param {String} name 事件名
 * @param {Function} callback 回调处理器
 * @returns {undefined}
 */
function addEvent(element, name, callback) {
	each(name.split(/\W+/), function(_, name) {
		if (element.addEventListener) {
			element.addEventListener(name, callback);
		} else if (element.attachEvent) {
			element.attachEvent("on" + name, callback);
		}
	});
}
/**
 * 预加载图片
 * @param {String} url
 * @param {Function} callback
 * @returns {Image}
 */
function prestrain(url, callback) {
	var img = new Image();
	addEvent(img, 'load', callback);
	addEvent(img, 'error', function() {
		console.error(url + ' loading fail!');
	});
	return img.src = url, img;
}
/**
 * 扩展对象
 * @param {Object} object
 * @param {Object} attach
 * @param {bool} clone
 * @returns {unresolved}
 */
function extend(object, attach, clone) {
	for (var attr in attach) {
		if (object[attr] === undefined) {
			if (clone && typeof attach[attr] === 'object') {
				if (attach[attr].length) {
					object[attr] = [];
				} else {
					object[attr] = {};
				}
				extend(object[attr], attach[attr], clone);
			} else {
				object[attr] = attach[attr];
				continue;
			}
		}
		if (typeof object[attr] === 'object') {
			extend(object[attr], attach[attr], clone);
		}
	}
	return object;
}
/**
 * 事件处理
 * @returns {Event}
 */
function Event() {
	this.listens = {};
}
Event.prototype = {
	/**
	 * 事件响应
	 * @param {String} name
	 * @param {Object} data
	 * @returns {undefined}
	 */
	fire: function(name, data) {
		var _this = this;
		each(this.listens[name] || {}, function(_, func) {
			if (func.apply(_this, data || []) === false) {
				return false;
			}
		});
	},
	/**
	 * 绑定事件
	 * @param {String} name
	 * @param {Function} callback
	 * @returns {undefined}
	 */
	on: function(name, callback) {
		if (!this.listens[name]) {
			this.listens[name] = [];
		}
		this.listens[name].push(callback);
	},
	/**
	 * 移除事件处理
	 * @param {String} name
	 * @param {Function|undefined} callback
	 * @returns {undefined}
	 */
	remove: function(name, callback) {
		if (callback) {
			each(this.listens[name] || {}, function(key, func) {
				if (func === callback) {
					this.listens[name].splice(key, 1);
					return false;
				}
			});
		} else {
			this.listens[name] = [];
		}
	}
};
/**
 * 动画初始化处理
 * @param {*} options 
 */
function animation(element, options) {
	Event.call(this);
	var status = 'stop',
		ready = 0,
		_this = this,
		facilitys = {},
		_end = false,
		loaded = false,
		parallel = true;
	each(options, function(facility, options) {
		if (typeof animation.facility[facility] === 'function') {
			ready++;
			var pay = animation.facility[facility](element, options, this, function() {
				ready--;
				readied();
			});
			if (pay instanceof AnimationPaly) {
				facilitys[facility] = pay;
				return;
			}
		}
		console.error(facility + ' animation is not exists');
	});
	/**
	 * 就绪处理
	 */
	function readied() {
		if (_end && ready < 1 && !loaded) {
			loaded = true;
			_this.fire('ready');
			status === 'play' && play();
		}
	}
	/**
	 * 播放处理
	 */
	function play() {
		if (!loaded) {
			return;
		}
		animation.remove(_this);
		animation.add(_this);
		_this.fire('play');
	}
	this.element = element;
	this.loop = false;
	_end = true;
	readied();
	/**
	 * 暂停动画
	 */
	this.pause = function() {
		if (status === 'play') {
			status = 'pause';
			this.fire('pause');
		}
	};
	/**
	 * 停止动画
	 */
	this.stop = function() {
		animation.remove(this);
		status = 'stop';
		this.fire('stop');
		each(facilitys, function(_, facility) {
			facility.reset();
		});
	};
	/**
	 * 播放动画
	 */
	this.play = function(from, loop, isParallel) {
		status = 'play';
		this.loop = loop === undefined ? this.loop : !!loop;
		each(from || {}, function(facility, options) {
			if (facilitys[facility]) {
				facilitys[facility].reset(options);
			}
		});
		parallel = isParallel === undefined ? parallel : !!isParallel;
		play();
	};
	/**
	 * 下一帧动画
	 */
	this.next = function() {
		if (status !== 'play') {
			return;
		}
		var played = true,
			_this = this;
		each(facilitys, function(_, facility) {
			facility.next(_this.loop);
			played = played && facility.played;
			if (!facility.played && !parallel) {
				return false;
			}
		});
		this.fire('next');
		if (played) {
			this.stop();
		}
	};
	/**
	 * 获取动画状态
	 */
	this.getStatus = function() {
		return status;
	};
	this.clone = function(element, event) {
		var _animation = new animation(element, options);
		if (event) {
			_animation.listens = this.listens;
		}
		return _animation;
	};
}
animation.prototype = new Event();
/**
 * 动画播放
 * @param {Function} to 
 */
function AnimationPaly(to, time, total) {
	var _next = 0;
	this.played = false;
	this.time = time;
	this.total = total;
	this.next = function(loop) {
		if (this.played) {
			return;
		}
		_next += (1000 / animation.getFrameRate());
		var index = Math.round(_next / (this.time / this.total));
		if (index >= this.total && !loop) {
			this.played = true;
			_next = 0;
		} else {
			to(index, this);
		}
	};
	this.reset = function(index) {
		_next = 0;
		this.played = false;
		to(index || 0, this);
	};
}
(function() {
	var status = 'stop',
		_frameRate = 40;
	/**
	 * 动画功能扩展
	 */
	extend(animation, {
		facility: {}, //动画功能
		depots: [], //动画集仓库
		/**
		 * 添加动画
		 * @param {*} _animation 
		 */
		add: function(_animation) {
			animation.depots.push(_animation);
		},
		/**
		 * 删除动画
		 * @param {*} _animation
		 */
		remove: function(_animation) {
			each(animation.depots, function(key, item) {
				if (item === _animation) {
					animation.depots.splice(key, 1);
					return false;
				}
			});
		},
		/**
		 * 暂停动画
		 */
		pause: function() {
			if (status === 'play') {
				status = 'pause';
			}
		},
		/**
		 * 停止动画
		 */
		stop: function() {
			status = 'stop';
		},
		/**
		 * 播放动画
		 */
		play: function() {
			status = 'play';
			setTimeout(function() {
				if (status === 'play') {
					each(animation.depots, function(_, animation) {
						animation.next();
					});
					animation.play();
				}
			}, 1000 / _frameRate);
		},
		/**
		 * 获取动画状态
		 */
		getStatus: function() {
			return status;
		},
		/**
		 * 扩展动画功能
		 */
		extend: function(facility) {
			extend(animation.facility, facility);
		},
		/**
		 * 获取帧率
		 */
		getFrameRate: function() {
			return _frameRate;
		},
		/**
		 * 设置帧率，最小1，最大60
		 * @param {number} frameRate 
		 */
		setFrameRate: function(frameRate) {
			if (typeof frameRate === 'number') {
				_frameRate = Math.max(Math.min(60, frameRate), 1);
			}
		}
	});
	animation.extend({
		/**
		 * 序列帧动画
		 */
		sequence: function(element, options, _animation, readied) {
			var images = [],
				index = 0,
				count = options.imgs.length,
				time = (options.time || 1) * 1000,
				img, ready = 0;
			each(options.imgs, function(_, src) {
				images.push(prestrain(src, function() {
					(++ready) >= count && readied();
				}));
			});
			append(element, img = new Image);
			show(0);

			function show(_index) {
				if (_index >= count) {
					_index = 0;
				}
				index = _index;
				if (images[index]) {
					img.src = images[index].src;
				}
			}
			// var canvas;
			// append(element, canvas = create('canvas'));
			// attr(canvas, { width: 750, height: 1334 });
			// var ctx = canvas.getContext("2d");
			// function show(_index) {
			//     if (_index >= count) {
			//         _index = 0;
			//     }
			//     index = _index;
			//     canvas.height = canvas.height;
			//     ctx.drawImage(images[index], 0, 0);
			// }
			return new AnimationPaly(show, time, count);
		},
		/**
		 * 样式动画
		 */
		css: function(element, options, _animation, readied) {
			return style(element, options, _animation, readied, css);
		},
		attr: function(element, options, _animation, readied) {
			return style(element, options, _animation, readied, attr);
		}
	});

	function style(element, options, _animation, readied, func) {
		var values = {},
			time = (options.time || 1) * 1000;
		each(options.data, function(name, value) {
			var from = parseInt(func(element, name) || 0),
				to = parseInt(value),
				length = (to - from),
				size = to >= from,
				unit = value.replace(/[-\.\d]+/, '');
			values[name] = function(index, play) {
				var _to = length / (play.time / animation.getFrameRate()) * index;
				if (options.ease) {
					_to = (options.ease(_to, 0, 1, length)) * length;
				} else {
					if (size ? _to > to : _to < to) {
						play.played = true;
						_to = to
					}
				}
				func(element, name, _to + unit);
			};
		});

		function show(index, play) {
			each(values, function(_, func) {
				func(index, play);
			});
		}
		readied();
		return new AnimationPaly(show, time, time / animation.getFrameRate());
	}
})();
/**
 * 音频播放
 * @param {Object} data 
 */
function audioPaly(data) {
	var depots = {},
		muted, total = 0,
		ready = 0,
		readyed = [],
		type = {
			'ogg': 'ogg',
			'mp3': 'mpeg',
			'wav': 'wav'
		};
	each(data, function(name, options) {
		var audio = new Audio();
		attr(audio, extend({
			preload: 'metadata'
		}, options.attr || {}));
		each(options.urls, function(_, url) {
			var ext = url.match(/\.(\w+$)/)[1];
			if (type[ext]) {
				audio.appendChild(create('source', {
					src: url,
					type: 'audio/' + type[ext]
				}));
			}
		});
		total++;
		depots[name] = depots[name] || [];
		addEvent(audio, 'loadedmetadata', function() {
			depots[name].push(audio);
			initAudio.listens.push(audio);
			ready++;
			loaded();
		});
		loaded();
	});

	function loaded() {
		if (ready >= total) {
			var func;
			while (func = readyed.shift()) {
				func();
			}
		}
	}
	extend(this, {
		play: function(name) {
			var audio = null;
			each(depots[name] || [], function(_, _audio) {
				if (_audio.paused) {
					_audio.play();
					audio = _audio;
					return false;
				}
			});
			if (!audio && depots[name] && depots[name][0]) {
				audio = depots[name][0].cloneNode(true);
				audio.currentTime = 0;
				audio.play();
				depots[name].push(audio);
			}
			return audio;
		},
		mute: function(status) {
			muted = !!status;
			each(depots, function(_, audios) {
				each(audios, function(_, audio) {
					audio.muted = muted;
				});
			});
		},
		ready: function(callback) {
			readyed.push(callback);
		}
	});
}
addEvent(document, 'touchstart', initAudio);

function initAudio() {
	var audio = initAudio.listens.shift();
	if (audio) {
		audio.paused && audio.play().then(function() {
			audio.pause();
		});
		initAudio();
	}
}
initAudio.listens = [];
export default {
	$animation: function(element, options) {
		return new animation(element, options);
	},
	$animationPaly: function(to, time, count) {
		return new AnimationPaly(to, time, count);
	},
	$Animation: animation,
	$audioPaly: function(data) {
		return new audioPaly(data);
	},
	css: css,
	attr: attr
};
